Озвучьте план лекции. Подчеркните, что начнём с низкоуровневых аппаратных решений и постепенно поднимемся к высокоуровневым абстракциям.
Начинаем с мотивации: зачем нужна синхронизация в многозадачных системах. Подчеркните, что гонки данных — не теоретическая проблема, а реальная угроза корректности программ.
Ключевое определение лекции. Нарисуйте схему: вход → критическая секция → выход → остаток. Именно эту структуру мы будем защищать всеми механизмами.
Три обязательных условия корректности. Подчеркните: все три должны выполняться одновременно. Аналогия — дверь в комнату: только один внутри, нельзя запереть навсегда, нельзя ждать бесконечно.
Конкретный пример. Покажите пошагово, как переключение контекста между чтением и записью приводит к потере обновления. Это ключевой мотивирующий пример всей лекции.
Переходим к аппаратным подходам. Объясните: процессор предоставляет базовые атомарные операции, на которых строятся все высокоуровневые примитивы синхронизации.
Простейший метод, но с серьёзными ограничениями. Подчеркните: в многопроцессорных SMP-системах отключение прерываний на одном ядре не мешает другому ядру обратиться к общим данным.
TSL — ключевая аппаратная инструкция. Объясните: атомарность означает, что чтение и запись выполняются как одна неразрывная операция — процессор гарантирует это аппаратно.
Покажите, как TSL строит spin lock — цикл активного ожидания. Процесс расходует процессорное время, пока ждёт — это главное ограничение подхода.
Альтернатива TSL. Swap атомарно обменивает значения, а логика ожидания реализуется через локальную переменную key в регистре процесса.
Переходим к чисто программным решениям. Алгоритм Петерсона не требует специальных аппаратных инструкций — только обычные чтение и запись в память.
Разберите код построчно. Ключевой момент: строка turn = process — процесс уступает очередь, а условие while проверяет и очередь, и флаг другого процесса.
Докажите корректность по трём условиям. Упомяните: для n процессов существует алгоритм пекарни Лампорта, но он значительно сложнее.
Центральная тема лекции. Эдсгер Дейкстра предложил семафоры в 1965 году — элегантная абстракция, скрывающая детали ожидания и пробуждения процессов.
Обратите внимание: отрицательное значение семафора — не ошибка, а число заблокированных процессов. Список ожидания обеспечивает пробуждение в порядке FIFO.
Два основных типа. Мьютекс — частный случай бинарного семафора. Считающий семафор удобен для управления пулами ресурсов с несколькими экземплярами.
Простейший паттерн: P перед критической секцией, V после. Начальное значение 1 разрешает вход одному процессу, остальные ждут.
Классическая задача синхронизации. Три семафора: mutex для защиты буфера, empty и full для координации состояний буфера между производителем и потребителем.
Разберите порядок операций. Производитель сначала ждёт пустой слот через P(&empty), потом захватывает мьютекс, заполняет слот и сигнализирует потребителю через V(&full).
Подчеркните: порядок P-операций критичен. Сначала проверяем наличие данных, потом захватываем мьютекс — иначе возможен взаимоблокировка.
Ключевой слайд. Покажите, как неправильный порядок приводит к deadlock: потребитель держит мьютекс и ждёт данные, а производитель не может положить данные — мьютекс занят.
Вторая классическая задача. Ключевое отличие: несколько читателей могут работать одновременно, но писателю нужен эксклюзивный доступ.
Объясните идею: первый читатель захватывает rw_mutex и блокирует писателей, последний читатель отпускает. Переменная read_count отслеживает число активных читателей.
Разберите код: первый читатель блокирует писателей через P(&rw_mutex), последний — разблокирует. Обратите внимание на защиту read_count мьютексом.
Писатель просто захватывает rw_mutex. Подчеркните проблему: если читатели приходят непрерывно и read_count не падает до нуля, писатель будет голодать бесконечно.
Решаем проблему голодания писателей. Дополнительный семафор w_mutex блокирует новых читателей, когда есть ожидающий писатель — это даёт писателю приоритет.
Обратите внимание: читатель сначала проходит через w_mutex, который может быть заблокирован ожидающим писателем. Именно это обеспечивает приоритет записи.
Двухфазная структура: регистрация через write_count и блокировка rw_mutex. Первый писатель блокирует читателей, последний — разблокирует.
Подведите итог: оба подхода решают задачу, но с разными приоритетами. Компромисс неизбежен — либо голодает писатель, либо читатель.
Переходим к высокоуровневым абстракциям. Монитор — как класс в ООП: данные + методы + встроенная синхронизация. Взаимное исключение гарантируется автоматически.
Ключевое отличие от семафоров: сигнал без ожидающих теряется бесследно. Это делает мониторы безопаснее — нельзя случайно накопить лишние разблокировки.
Сравните с решением через семафоры — код значительно проще. Взаимное исключение встроено, остаётся только логика ожидания условия.
Обратите внимание: signal(ok_read) и signal(ok_write) будят и читателей, и писателей. Взаимное исключение гарантируется монитором — не нужно думать о мьютексах.
Подведите итог сравнения. Семафоры мощнее и гибче, но мониторы безопаснее и проще. На практике мониторы предпочтительнее, где это возможно.
Две ключевые проблемы: голодание и взаимная блокировка. Условия Коффмана — чеклист из четырёх условий, необходимых для возникновения deadlock.
Четыре столпа проектирования. Подчеркните: на практике часто приходится искать баланс между эффективностью и справедливостью.
Переходим к реальным API. Покажите, что теоретические семафоры воплощены в конкретных системных вызовах и библиотеках, с которыми студенты будут работать.
System V IPC — классический интерфейс семафоров в UNIX. Объясните структуру sembuf: номер семафора, операция (+1/-1), флаги.
Наиболее частый паттерн в пользовательских программах. Подчеркните: мьютекс нужно инициализировать и обязательно уничтожать при завершении.
Покажите паттерн: мьютекс + условная переменная. Подчеркните, что проверка условия в цикле while, а не в if — защита от ложных пробуждений (spurious wakeup).
Обобщающая таблица. Покажите эволюцию: от аппаратных инструкций к библиотечным абстракциям. Чем выше уровень, тем безопаснее и проще использование.
Загляните в будущее: lock-free алгоритмы и аппаратная транзакционная память. Подчеркните: проблема синхронизации остаётся актуальной и активно развивается.
Подведите итог лекции: от TSL до мониторов — эволюция от простых к сложным. Каждый уровень абстракции решает проблемы предыдущего.
Резюме главных тезисов лекции. Обратите внимание студентов на вопросы для самоконтроля — они помогут закрепить материал.
Порекомендуйте студентам проработать каждый вопрос. Особенно важны вопросы 4, 7 и 10 — они проверяют глубокое понимание механизмов синхронизации.
Порекомендуйте Таненбаума как основной учебник, а статью Дейкстры 1965 года — для исторического контекста и глубокого понимания семафоров.